/* -*- c++ -*- */
/*
 * This file is part of the SD Project.
 *
 * Authors: Cleiber Marques da Silva <cleiber@lisha.ufsc.br>
 * 			Odilson Tadeu Valle <odilson@sj.ifsc.edu.br>
 * 
 * SD Project is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the License.
 */

#include <iostream>
#include <gtkmm.h>

#include "listener.hh"
#include "listener_impl.h"
#include "clock.h"
#include "window.h"
#include "delivery.h"
#include "send.h"

static CORBA::Boolean bindObjectToName(CORBA::ORB_ptr, CORBA::Object_ptr, const char *id);

int main(int argc, char** argv)
{
  	std::string id = argv[1];
	std::vector<std::string> ids;
	ids.push_back("1");
	ids.push_back("2");
	ids.push_back("3");
	sd::clock *clock = new sd::clock(ids.size());

	Gtk::Main kit(argc, argv);
	sd::window *window = new sd::window();

	sd::delivery *delivery = new sd::delivery(ids.size(), window);

  try {
    CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

    CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
    PortableServer::POA_var poa = PortableServer::POA::_narrow(obj);

    sd::listener_impl* listen = new sd::listener_impl(clock, delivery);

    PortableServer::ObjectId_var listen_id = poa->activate_object(listen);

    // Obtain a reference to the object, and register it in
    // the naming service.
    obj = listen->_this();

    CORBA::String_var x;
    x = orb->object_to_string(obj);
    std::cout << x << std::endl;

    if( !bindObjectToName(orb, obj, id.c_str()) )
      return 1;

    listen->_remove_ref();

    PortableServer::POAManager_var pman = poa->the_POAManager();
    pman->activate();
	
	sd::send *emmiter = new sd::send(argc, argv, clock, delivery, window, id, ids);
	emmiter->start();

    orb->run();
  }
  catch(CORBA::SystemException& ex) {
    std::cerr << "Caught CORBA::" << ex._name() << std::endl;
  }
  catch(CORBA::Exception& ex) {
    std::cerr << "Caught CORBA::Exception: " << ex._name() << std::endl;
  }
  catch(omniORB::fatalException& fe) {
    std::cerr << "Caught omniORB::fatalException:" << std::endl;
    std::cerr << "  file: " << fe.file() << std::endl;
    std::cerr << "  line: " << fe.line() << std::endl;
    std::cerr << "  mesg: " << fe.errmsg() << std::endl;
  }
  return 0;
}

static CORBA::Boolean
bindObjectToName(CORBA::ORB_ptr orb, CORBA::Object_ptr objref, const char *id)
{
  CosNaming::NamingContext_var rootContext;

  try {
    // Obtain a reference to the root context of the Name service:
    CORBA::Object_var obj;
    obj = orb->resolve_initial_references("NameService");

    // Narrow the reference returned.
    rootContext = CosNaming::NamingContext::_narrow(obj);
    if( CORBA::is_nil(rootContext) ) {
      std::cerr << "Failed to narrow the root naming context." << std::endl;
      return 0;
    }
  }
  catch (CORBA::NO_RESOURCES&) {
    std::cerr << "Caught NO_RESOURCES exception. You must configure omniORB "
	 << "with the location" << std::endl
	 << "of the naming service." << std::endl;
    return 0;
  }
  catch (CORBA::ORB::InvalidName&) {
    // This should not happen!
    std::cerr << "Service required is invalid [does not exist]." << std::endl;
    return 0;
  }

  try {

    // Bind objref with name Echo to the testContext:
    CosNaming::Name objectName;
    objectName.length(1);
    objectName[0].id   = CORBA::string_dup(id);
    //objectName[0].kind = (const char*) "Object";

    try {
      rootContext->bind(objectName, objref);
    }
    catch(CosNaming::NamingContext::AlreadyBound& ex) {
     	rootContext->rebind(objectName, objref);
    }
    // Note: Using rebind() will overwrite any Object previously bound
    //       to /test/Echo with obj.
    //       Alternatively, bind() can be used, which will raise a
    //       CosNaming::NamingContext::AlreadyBound exception if the name
    //       supplied is already bound to an object.

    // Amendment: When using OrbixNames, it is necessary to first try bind
    // and then rebind, as rebind on it's own will throw a NotFoundexception if
    // the Name has not already been bound. [This is incorrect behaviour -
    // it should just bind].
  }
  catch(CORBA::TRANSIENT& ex) {
    std::cerr << "Caught system exception TRANSIENT -- unable to contact the "
         << "naming service." << std::endl
	 << "Make sure the naming server is running and that omniORB: is "
	 << "configured correctly." << std::endl;

    return 0;
  }
  catch(CORBA::SystemException& ex) {
    std::cerr << "Caught a CORBA::" << ex._name()
	 << " while using the naming service." << std::endl;
    return 0;
  }
  return 1;
}

